from functools import wraps


def notify(function):
    """Decorator to mark function as able to have observers.
    Decorated function returns list where the first value is return of
    original function and next values are outputs of observers.
    """
    function._observers = []
    function._instance_observers = {}

    @wraps(function)
    def decorated(*args, **kwargs):
        ret = []
        for observer in function._observers:
            ret.append(observer(*args, **kwargs))
        instance = None
        if len(args) > 0 and args[0] in function._instance_observers:
            instance = args[0]
        if "self" in kwargs and kwargs["self"] in function._instance_observers:
            instance = kwargs["self"]
        if instance:
            for observer in function._instance_observers[instance]:
                ret.append(observer(*args, **kwargs))
        ret.insert(0, function(*args, **kwargs))
        return ret

    def add_observer(observer, instance=None):
        """Adds observer to this function.
        If it is method and you want to observe only specific instance use
        use instance argument.
        """
        if instance is None:
            function._observers.append(observer)
        else:
            if not instance in function._instance_observers:
                function._instance_observers[instance] = []
            function._instance_observers[instance].append(observer)
    decorated.add_observer = add_observer

    def del_observer(observer, instance=None):
        """Removes observer from this function."""
        if instance is None:
            function._observers.remove(observer)
        else:
            function._instance_observers[instance].remove(observer)
    decorated.del_observer = del_observer

    return decorated
